#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
3D魔方可视化程序
使用OpenGL和pygame创建交互式3D魔方显示
特点：只有中心色块显示原色，其他色块显示灰色
支持鼠标拖拽旋转视角和键盘控制
"""

# 导入必要的库
try:
    from OpenGL.GL import *
    from OpenGL.GLU import *
    import pygame
    from pygame.locals import *
except ImportError:
    print("需要安装OpenGL和pygame库:")
    print("pip install PyOpenGL PyOpenGL_accelerate pygame")
    exit(1)

class RubiksCube3D:
    """
    3D魔方可视化类
    
    功能特点：
    - 渲染3x3x3魔方，只有中心色块显示原色
    - 支持鼠标拖拽旋转视角
    - 支持键盘控制和自动旋转
    - 使用OpenGL进行3D渲染
    """
    
    def __init__(self, width=800, height=600):
        """
        初始化3D魔方显示器
        
        Args:
            width (int): 窗口宽度，默认800
            height (int): 窗口高度，默认600
        """
        # 窗口尺寸
        self.width = width
        self.height = height
        
        # 旋转控制变量 - 初始视角设置为绿白红三面交汇处
        self.rotation_x = 30  # X轴旋转角度
        self.rotation_y = -45   # Y轴旋转角度
        self.rotation_speed = 2  # 自动旋转速度
        self.auto_rotate = False  # 是否自动旋转
        
        # 魔方面旋转动画控制
        self.is_animating = False     # 是否正在播放旋转动画
        self.animation_face = None    # 当前旋转的面
        self.animation_angle = 0.0    # 当前动画角度
        self.animation_speed = 5.0    # 动画速度（度/帧）
        self.animation_target = 90.0  # 目标角度（90度为一次旋转）
        self.animation_clockwise = True  # 旋转方向
        
        # 整体旋转动画控制
        self.is_view_animating = False  # 是否正在播放视角旋转动画
        self.view_animation_axis = None  # 旋转轴（'x' 或 'y'）
        self.view_animation_start_x = 0.0  # X轴起始角度
        self.view_animation_start_y = 0.0  # Y轴起始角度
        self.view_animation_target_x = 0.0  # X轴目标角度
        self.view_animation_target_y = 0.0  # Y轴目标角度
        self.view_animation_progress = 0.0  # 动画进度（0.0-1.0）
        self.view_animation_speed = 0.05  # 视角动画速度
        
        # 魔方几何参数
        self.cube_size = 0.3  # 单个小立方体的尺寸
        self.gap = 0.05  # 小立方体之间的间隙
        
        # 魔方标准颜色定义（国际标准配色）
        self.colors = {
            'white': (1.0, 1.0, 1.0),    # 白色 - 上面
            'red': (1.0, 0.0, 0.0),      # 红色 - 前面
            'blue': (0.0, 0.0, 1.0),     # 蓝色 - 右面
            'orange': (1.0, 0.5, 0.0),   # 橙色 - 后面
            'green': (0.0, 1.0, 0.0),    # 绿色 - 左面
            'yellow': (1.0, 1.0, 0.0),   # 黄色 - 下面
            'black': (0.0, 0.0, 0.0)     # 黑色 - 边框
        }
        
        # 初始化pygame和OpenGL显示
        self._init_display()
        
    def _init_display(self):
        """
        初始化pygame和OpenGL显示环境
        
        设置窗口、OpenGL状态、光照和投影矩阵
        """
        # 初始化pygame
        pygame.init()
        
        # 设置OpenGL属性以支持透明度
        pygame.display.gl_set_attribute(pygame.GL_ALPHA_SIZE, 8)
        pygame.display.set_mode((self.width, self.height), DOUBLEBUF | OPENGL)
        pygame.display.set_caption("3D魔方可视化 - 中心色块模式")
        
        # 启用OpenGL功能
        glEnable(GL_DEPTH_TEST)      # 启用深度测试（Z缓冲）
        
        # 启用混合功能，支持透明度渲染
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        
        glEnable(GL_LIGHTING)        # 启用光照
        glEnable(GL_LIGHT0)          # 启用0号光源
        glEnable(GL_COLOR_MATERIAL)  # 启用颜色材质
        
        # 配置光照参数
        glLightfv(GL_LIGHT0, GL_POSITION, [1, 1, 1, 0])      # 光源位置（方向光）
        glLightfv(GL_LIGHT0, GL_AMBIENT, [0.3, 0.3, 0.3, 1]) # 环境光强度
        glLightfv(GL_LIGHT0, GL_DIFFUSE, [0.8, 0.8, 0.8, 1]) # 漫反射光强度
        
        # 设置背景颜色为透明
        glClearColor(0.0, 0.0, 0.0, 0.0)  # RGBA: 完全透明的背景
        
        # 设置投影矩阵
        glMatrixMode(GL_PROJECTION)
        gluPerspective(45, (self.width / self.height), 0.1, 50.0)  # 透视投影
        glMatrixMode(GL_MODELVIEW)
        
        # 设置初始视角位置
        glTranslatef(0.0, 0.0, -5)  # 向后移动5个单位，以便看到魔方
    
    def draw_small_cube(self, x, y, z, i, j, k):
        """
        绘制单个小立方体
        
        Args:
            x, y, z (float): 立方体在3D空间中的位置坐标
            i, j, k (int): 立方体在魔方中的索引位置 (0-2)
                          i: X轴索引 (0=左, 1=中, 2=右)
                          j: Y轴索引 (0=下, 1=中, 2=上)
                          k: Z轴索引 (0=后, 1=中, 2=前)
        
        特点：只有中心色块显示原色，其他色块显示灰色
        支持面旋转动画效果
        """
        size = self.cube_size
        
        # 检查当前小立方体是否属于正在旋转的面
        should_rotate = False
        if self.is_animating and self.animation_face:
            if self.animation_face == 'left' and i == 0:    # L面：X=0
                should_rotate = True
            elif self.animation_face == 'right' and i == 2:  # R面：X=2
                should_rotate = True
            elif self.animation_face == 'bottom' and j == 0: # D面：Y=0
                should_rotate = True
            elif self.animation_face == 'top' and j == 2:    # U面：Y=2
                should_rotate = True
            elif self.animation_face == 'back' and k == 0:   # B面：Z=0
                should_rotate = True
            elif self.animation_face == 'front' and k == 2:  # F面：Z=2
                should_rotate = True
        
        # 如果需要旋转，应用旋转变换
        if should_rotate:
            glPushMatrix()
            
            # 移动到旋转中心
            if self.animation_face == 'left':
                # L面：绕X轴正方向旋转
                glTranslatef(0, 0, 0)  # 旋转中心在原点
                glRotatef(self.animation_angle, 1, 0, 0)
            elif self.animation_face == 'right':
                # R面：绕X轴负方向旋转（从右侧看顺时针）
                glTranslatef(0, 0, 0)
                glRotatef(-self.animation_angle, 1, 0, 0)
            elif self.animation_face == 'bottom':
                # D面：绕Y轴正方向旋转（从下方看顺时针）
                glTranslatef(0, 0, 0)
                glRotatef(self.animation_angle, 0, 1, 0)
            elif self.animation_face == 'top':
                # U面：绕Y轴负方向旋转（从上方看顺时针）
                glTranslatef(0, 0, 0)
                glRotatef(-self.animation_angle, 0, 1, 0)
            elif self.animation_face == 'back':
                # B面：绕Z轴正方向旋转（从后方看顺时针）
                glTranslatef(0, 0, 0)
                glRotatef(self.animation_angle, 0, 0, 1)
            elif self.animation_face == 'front':
                # F面：绕Z轴正方向旋转（从前方看顺时针）
                glTranslatef(0, 0, 0)
                glRotatef(self.animation_angle, 0, 0, 1)
            
            # 移动到立方体位置
            glTranslatef(x, y, z)
        
        # 魔方六个面的标准颜色映射
        face_colors = {
            'front': 'green',   # Z+ 前面 - 绿色
            'back': 'blue',     # Z- 后面 - 蓝色
            'top': 'white',     # Y+ 上面 - 白色
            'bottom': 'yellow', # Y- 下面 - 黄色
            'right': 'red',     # X+ 右面 - 红色
            'left': 'orange'    # X- 左面 - 橙色
        }
        
        # 判断每个面是否可见（位于魔方外表面）
        visible_faces = {}
        if k == 2: visible_faces['front'] = True   # 前面可见（Z轴最大值）
        if k == 0: visible_faces['back'] = True    # 后面可见（Z轴最小值）
        if j == 2: visible_faces['top'] = True     # 上面可见（Y轴最大值）
        if j == 0: visible_faces['bottom'] = True  # 下面可见（Y轴最小值）
        if i == 2: visible_faces['right'] = True   # 右面可见（X轴最大值）
        if i == 0: visible_faces['left'] = True    # 左面可见（X轴最小值）
        
        def is_center_piece(face_name):
            """
            判断当前小立方体是否为指定面的中心色块
            
            中心色块的特征：在该面上且位于面的中心位置(1,1)
            
            Args:
                face_name (str): 面的名称
            
            Returns:
                bool: 是否为中心色块
            """
            if face_name == 'front' and k == 2 and i == 1 and j == 1:
                return True  # 前面中心
            if face_name == 'back' and k == 0 and i == 1 and j == 1:
                return True  # 后面中心
            if face_name == 'top' and j == 2 and i == 1 and k == 1:
                return True  # 上面中心
            if face_name == 'bottom' and j == 0 and i == 1 and k == 1:
                return True  # 下面中心
            if face_name == 'right' and i == 2 and j == 1 and k == 1:
                return True  # 右面中心
            if face_name == 'left' and i == 0 and j == 1 and k == 1:
                return True  # 左面中心
            return False
        
        # 如果不需要旋转，直接移动到位置
        if not should_rotate:
            glPushMatrix()
            glTranslatef(x, y, z)
        
        # 绘制立方体的6个面，每个面使用对应的颜色
        
        # 绘制前面 (Z+方向) - 魔方的正前方
        if visible_faces.get('front', False):
            # 可见面：根据是否为中心色块决定颜色
            if is_center_piece('front'):
                glColor3f(*self.colors[face_colors['front']])  # 中心色块显示原色（绿色）
            else:
                glColor3f(0.5, 0.5, 0.5)  # 非中心色块显示灰色
        else:
            # 内部面：显示深灰色
            glColor3f(0.2, 0.2, 0.2)
        glBegin(GL_QUADS)
        glNormal3f(0, 0, 1)  # 设置法向量指向Z+方向
        glVertex3f(-size, -size, size)  # 左下角顶点
        glVertex3f(size, -size, size)   # 右下角顶点
        glVertex3f(size, size, size)    # 右上角顶点
        glVertex3f(-size, size, size)   # 左上角顶点
        glEnd()
        
        # 绘制后面 (Z-方向) - 魔方的正后方
        if visible_faces.get('back', False):
            # 可见面：根据是否为中心色块决定颜色
            if is_center_piece('back'):
                glColor3f(*self.colors[face_colors['back']])  # 中心色块显示原色（蓝色）
            else:
                glColor3f(0.5, 0.5, 0.5)  # 非中心色块显示灰色
        else:
            # 内部面：显示深灰色
            glColor3f(0.2, 0.2, 0.2)
        glBegin(GL_QUADS)
        glNormal3f(0, 0, -1)  # 设置法向量指向Z-方向
        glVertex3f(-size, -size, -size)  # 左下角顶点
        glVertex3f(-size, size, -size)   # 左上角顶点
        glVertex3f(size, size, -size)    # 右上角顶点
        glVertex3f(size, -size, -size)   # 右下角顶点
        glEnd()
        
        # 绘制左面 (X-方向) - 魔方的左侧面
        if visible_faces.get('left', False):
            # 可见面：根据是否为中心色块决定颜色
            if is_center_piece('left'):
                glColor3f(*self.colors[face_colors['left']])  # 中心色块显示原色（橙色）
            else:
                glColor3f(0.5, 0.5, 0.5)  # 非中心色块显示灰色
        else:
            # 内部面：显示深灰色
            glColor3f(0.2, 0.2, 0.2)
        glBegin(GL_QUADS)
        glNormal3f(-1, 0, 0)  # 设置法向量指向X-方向
        glVertex3f(-size, -size, -size)  # 后下角顶点
        glVertex3f(-size, -size, size)   # 前下角顶点
        glVertex3f(-size, size, size)    # 前上角顶点
        glVertex3f(-size, size, -size)   # 后上角顶点
        glEnd()
        
        # 绘制右面 (X+方向) - 魔方的右侧面
        if visible_faces.get('right', False):
            # 可见面：根据是否为中心色块决定颜色
            if is_center_piece('right'):
                glColor3f(*self.colors[face_colors['right']])  # 中心色块显示原色（红色）
            else:
                glColor3f(0.5, 0.5, 0.5)  # 非中心色块显示灰色
        else:
            # 内部面：显示深灰色
            glColor3f(0.2, 0.2, 0.2)
        glBegin(GL_QUADS)
        glNormal3f(1, 0, 0)  # 设置法向量指向X+方向
        glVertex3f(size, -size, -size)  # 后下角顶点
        glVertex3f(size, size, -size)   # 后上角顶点
        glVertex3f(size, size, size)    # 前上角顶点
        glVertex3f(size, -size, size)   # 前下角顶点
        glEnd()
        
        # 绘制上面 (Y+方向) - 魔方的顶面
        if visible_faces.get('top', False):
            # 可见面：根据是否为中心色块决定颜色
            if is_center_piece('top'):
                glColor3f(*self.colors[face_colors['top']])  # 中心色块显示原色（白色）
            else:
                glColor3f(0.5, 0.5, 0.5)  # 非中心色块显示灰色
        else:
            # 内部面：显示深灰色
            glColor3f(0.2, 0.2, 0.2)
        glBegin(GL_QUADS)
        glNormal3f(0, 1, 0)  # 设置法向量指向Y+方向
        glVertex3f(-size, size, -size)  # 左后角顶点
        glVertex3f(-size, size, size)   # 左前角顶点
        glVertex3f(size, size, size)    # 右前角顶点
        glVertex3f(size, size, -size)   # 右后角顶点
        glEnd()
        
        # 绘制下面 (Y-方向) - 魔方的底面
        if visible_faces.get('bottom', False):
            # 可见面：根据是否为中心色块决定颜色
            if is_center_piece('bottom'):
                glColor3f(*self.colors[face_colors['bottom']])  # 中心色块显示原色（黄色）
            else:
                glColor3f(0.5, 0.5, 0.5)  # 非中心色块显示灰色
        else:
            # 内部面：显示深灰色
            glColor3f(0.2, 0.2, 0.2)
        glBegin(GL_QUADS)
        glNormal3f(0, -1, 0)  # 设置法向量指向Y-方向
        glVertex3f(-size, -size, -size)  # 左后角顶点
        glVertex3f(size, -size, -size)   # 右后角顶点
        glVertex3f(size, -size, size)    # 右前角顶点
        glVertex3f(-size, -size, size)   # 左前角顶点
        glEnd()
        
        # 绘制立方体的边缘线条（黑色边框）
        # 这些线条勾勒出每个小立方体的轮廓，使魔方结构更清晰
        glColor3f(0.0, 0.0, 0.0)  # 设置线条颜色为黑色
        glLineWidth(2.0)  # 设置线条宽度为2像素
        
        # 绘制立方体的12条边线，构成完整的线框
        glBegin(GL_LINES)
        
        # 绘制前面（Z+）的4条边框
        glVertex3f(-size, -size, size)  # 底边：左下 -> 右下
        glVertex3f(size, -size, size)
        
        glVertex3f(size, -size, size)   # 右边：右下 -> 右上
        glVertex3f(size, size, size)
        
        glVertex3f(size, size, size)    # 顶边：右上 -> 左上
        glVertex3f(-size, size, size)
        
        glVertex3f(-size, size, size)   # 左边：左上 -> 左下
        glVertex3f(-size, -size, size)
        
        # 绘制后面（Z-）的4条边框
        glVertex3f(-size, -size, -size)  # 底边：左下 -> 右下
        glVertex3f(size, -size, -size)
        
        glVertex3f(size, -size, -size)   # 右边：右下 -> 右上
        glVertex3f(size, size, -size)
        
        glVertex3f(size, size, -size)    # 顶边：右上 -> 左上
        glVertex3f(-size, size, -size)
        
        glVertex3f(-size, size, -size)   # 左边：左上 -> 左下
        glVertex3f(-size, -size, -size)
        
        # 绘制连接前后面的4条深度边框（Z方向）
        glVertex3f(-size, -size, -size)  # 左下角：后 -> 前
        glVertex3f(-size, -size, size)
        
        glVertex3f(size, -size, -size)   # 右下角：后 -> 前
        glVertex3f(size, -size, size)
        
        glVertex3f(size, size, -size)    # 右上角：后 -> 前
        glVertex3f(size, size, size)
        
        glVertex3f(-size, size, -size)   # 左上角：后 -> 前
        glVertex3f(-size, size, size)
        
        glEnd()
        
        # 恢复矩阵状态
        glPopMatrix()
    
    def draw_cube(self):
        """
        绘制整个3x3x3魔方
        
        该方法负责：
        1. 计算小立方体的间距
        2. 遍历并绘制所有27个小立方体
        3. 绘制魔方的缝隙和边框
        
        特点：只有中心色块显示原色，其他色块显示灰色
        """
        # 计算小立方体之间的间距
        # spacing = 立方体尺寸 * 2 + 间隙，确保立方体之间有适当的分离
        spacing = self.cube_size * 2 + self.gap
        
        # 绘制3x3x3=27个小立方体
        for i in range(3):  # X轴方向：0=左, 1=中, 2=右
            for j in range(3):  # Y轴方向：0=下, 1=中, 2=上
                for k in range(3):  # Z轴方向：0=后, 1=中, 2=前
                    # 计算每个小立方体的3D位置坐标
                    # 使用(i-1)将索引从[0,1,2]转换为[-1,0,1]，实现中心化布局
                    x = (i - 1) * spacing  # X坐标：左(-spacing) 中(0) 右(+spacing)
                    y = (j - 1) * spacing  # Y坐标：下(-spacing) 中(0) 上(+spacing)
                    z = (k - 1) * spacing  # Z坐标：后(-spacing) 中(0) 前(+spacing)
                    
                    # 绘制单个小立方体，传递位置坐标和索引
                    # 索引用于判断是否为中心色块
                    self.draw_small_cube(x, y, z, i, j, k)
        
        # 绘制魔方的黑色缝隙和边框，增强视觉效果
        self.draw_gaps()
    

    
    def draw_gaps(self):
        """
        绘制魔方各个小立方体之间的黑色缝隙
        
        该方法在魔方的各个表面绘制黑色的分隔条，模拟真实魔方中
        各个小立方体之间的缝隙。这些缝隙不仅增强了视觉效果，
        也使魔方的3x3x3结构更加清晰可见。
        
        绘制内容：
        1. X方向的2条垂直分隔条（将魔方分成3列）
        2. Y方向的2条水平分隔条（将魔方分成3行）
        3. 每条分隔条在魔方的多个表面都有对应的绘制
        
        技术细节：
        - 使用GL_QUADS绘制矩形缝隙条
        - 添加微小的Z偏移(±0.001)避免Z-fighting问题
        - 缝隙宽度为设定间隙的一半，确保适当的视觉效果
        """
        gap_width = self.gap / 2  # 缝隙宽度为间隙的一半，确保缝隙不会太宽
        spacing = self.cube_size * 2 + self.gap  # 小立方体中心之间的距离
        cube_extent = spacing - self.cube_size  # 魔方外表面的范围边界
        
        glColor3f(0.0, 0.0, 0.0)  # 设置缝隙颜色为黑色，模拟真实魔方的分隔线
        
        # 绘制X方向的缝隙（垂直分割线）
        # 这些缝隙将魔方在X轴方向分成3列
        for i in range(2):  # 需要2条垂直线来分成3列
            # 计算缝隙的X坐标位置
            # (i - 0.5) 将索引转换为 [-0.5, 0.5]，对应两条分隔线的位置
            x = (i - 0.5) * spacing
            
            glBegin(GL_QUADS)
            
            # 在前面（Z = cube_extent）绘制垂直缝隙条
            glVertex3f(x - gap_width, -cube_extent, cube_extent + 0.001)  # 左下角
            glVertex3f(x + gap_width, -cube_extent, cube_extent + 0.001)  # 右下角
            glVertex3f(x + gap_width, cube_extent, cube_extent + 0.001)   # 右上角
            glVertex3f(x - gap_width, cube_extent, cube_extent + 0.001)   # 左上角
            
            # 在后面（Z = -cube_extent）绘制垂直缝隙条
            glVertex3f(x - gap_width, -cube_extent, -cube_extent - 0.001) # 左下角
            glVertex3f(x - gap_width, cube_extent, -cube_extent - 0.001)  # 左上角
            glVertex3f(x + gap_width, cube_extent, -cube_extent - 0.001)  # 右上角
            glVertex3f(x + gap_width, -cube_extent, -cube_extent - 0.001) # 右下角
            
            # 在上面（Y = cube_extent）绘制垂直缝隙条
            glVertex3f(x - gap_width, cube_extent + 0.001, -cube_extent) # 后左角
            glVertex3f(x + gap_width, cube_extent + 0.001, -cube_extent) # 后右角
            glVertex3f(x + gap_width, cube_extent + 0.001, cube_extent)  # 前右角
            glVertex3f(x - gap_width, cube_extent + 0.001, cube_extent)  # 前左角
            
            # 在下面（Y = -cube_extent）绘制垂直缝隙条
            glVertex3f(x - gap_width, -cube_extent - 0.001, -cube_extent) # 后左角
            glVertex3f(x - gap_width, -cube_extent - 0.001, cube_extent)  # 前左角
            glVertex3f(x + gap_width, -cube_extent - 0.001, cube_extent)  # 前右角
            glVertex3f(x + gap_width, -cube_extent - 0.001, -cube_extent) # 后右角
            
            glEnd()
        
        # 绘制Y方向的缝隙（水平分割线）
        # 这些缝隙将魔方在Y轴方向分成3行
        for j in range(2):  # 需要2条水平线来分成3行
            # 计算缝隙的Y坐标位置
            # (j - 0.5) 将索引转换为 [-0.5, 0.5]，对应两条分隔线的位置
            y = (j - 0.5) * spacing
            
            glBegin(GL_QUADS)
            
            # 在前面（Z = cube_extent）绘制水平缝隙条
            glVertex3f(-cube_extent, y - gap_width, cube_extent + 0.001) # 左下角
            glVertex3f(cube_extent, y - gap_width, cube_extent + 0.001)  # 右下角
            glVertex3f(cube_extent, y + gap_width, cube_extent + 0.001)  # 右上角
            glVertex3f(-cube_extent, y + gap_width, cube_extent + 0.001) # 左上角
            
            # 在后面（Z = -cube_extent）绘制水平缝隙条
            glVertex3f(-cube_extent, y - gap_width, -cube_extent - 0.001) # 左下角
            glVertex3f(-cube_extent, y + gap_width, -cube_extent - 0.001) # 左上角
            glVertex3f(cube_extent, y + gap_width, -cube_extent - 0.001)  # 右上角
            glVertex3f(cube_extent, y - gap_width, -cube_extent - 0.001)  # 右下角
            
            # 在左面（X = -cube_extent）绘制水平缝隙条
            glVertex3f(-cube_extent - 0.001, y - gap_width, -cube_extent) # 后下角
            glVertex3f(-cube_extent - 0.001, y - gap_width, cube_extent)  # 前下角
            glVertex3f(-cube_extent - 0.001, y + gap_width, cube_extent)  # 前上角
            glVertex3f(-cube_extent - 0.001, y + gap_width, -cube_extent) # 后上角
            
            # 在右面（X = cube_extent）绘制水平缝隙条
            glVertex3f(cube_extent + 0.001, y - gap_width, -cube_extent) # 后下角
            glVertex3f(cube_extent + 0.001, y + gap_width, -cube_extent) # 后上角
            glVertex3f(cube_extent + 0.001, y + gap_width, cube_extent)  # 前上角
            glVertex3f(cube_extent + 0.001, y - gap_width, cube_extent)  # 前下角
            
            glEnd()
    
    def start_face_rotation(self, face, clockwise=True):
        """
        启动魔方面的旋转动画
        
        Args:
            face (str): 要旋转的面名称 ('front', 'back', 'left', 'right', 'top', 'bottom')
            clockwise (bool): 是否顺时针旋转，默认为True
        
        Returns:
            bool: 是否成功启动动画（如果已有动画在播放则返回False）
        """
        # 如果已有动画在播放，则忽略新的旋转请求
        if self.is_animating:
            print(f"动画正在播放中，忽略 {face} 面旋转请求")
            return False
        
        # 启动新的旋转动画
        self.is_animating = True
        self.animation_face = face
        self.animation_angle = 0.0
        self.animation_clockwise = clockwise
        self.animation_target = 90.0
        
        print(f"开始旋转 {face} 面 {'顺时针' if clockwise else '逆时针'}")
        return True
    
    def start_view_rotation(self, axis, direction):
        """
        启动整体视角旋转动画
        
        Args:
            axis (str): 旋转轴 ('x' 或 'y')
            direction (int): 旋转方向 (1为正方向, -1为负方向)
            对于固定路线：1表示绿→白→蓝→黄，-1表示绿→黄→蓝→白
        """
        # 如果已经在播放视角动画，忽略新的旋转请求
        if self.is_view_animating:
            print("正在播放视角旋转动画，忽略新的旋转请求")
            return
        
        # 设置动画参数
        self.is_view_animating = True
        self.view_animation_axis = axis
        self.view_animation_progress = 0.0
        
        # 记录起始角度
        self.view_animation_start_x = self.rotation_x
        self.view_animation_start_y = self.rotation_y
        
        # 设置目标角度
        if axis == 'x':
            # 上下方向键：实现固定的颜色路线旋转
            # 绿→白→蓝→黄 (direction=1) 或 绿→黄→蓝→白 (direction=-1)
            if direction == 1:
                # 上方向键：绿→白→蓝→黄路线
                # 需要绕X轴旋转90度使前面从绿色变为白色
                self.view_animation_target_x = self.rotation_x - 90
                self.view_animation_target_y = self.rotation_y
            else:
                # 下方向键：绿→黄→蓝→白路线  
                # 需要绕X轴旋转-90度使前面从绿色变为黄色
                self.view_animation_target_x = self.rotation_x + 90
                self.view_animation_target_y = self.rotation_y
        else:  # axis == 'y'
            # 左右方向键：保持原有的左右旋转逻辑
            self.view_animation_target_x = self.rotation_x
            self.view_animation_target_y = self.rotation_y + (90 * direction)
        
        route_desc = "绿→白→蓝→黄" if direction == 1 else "绿→黄→蓝→白"
        print(f"开始{'固定路线' if axis == 'x' else '左右'}旋转动画: {route_desc if axis == 'x' else '左右旋转'}")
    
    def rotate_face(self, face, clockwise=True):
        """
        旋转魔方的指定面（兼容性方法）
        
        这个方法现在会启动动画而不是立即旋转
        
        Args:
            face (str): 要旋转的面名称 ('front', 'back', 'left', 'right', 'top', 'bottom')
            clockwise (bool): 是否顺时针旋转，默认为True
        """
        self.start_face_rotation(face, clockwise)
    
    def handle_events(self):
        """
        处理用户输入事件
        
        支持的交互方式：
        1. 键盘控制：ESC退出、空格切换自动旋转、字母键旋转面
        2. 鼠标控制：左键拖拽旋转视角
        3. 窗口事件：关闭按钮退出
        
        Returns:
            bool: 是否继续运行程序，False表示退出
        """
        # 遍历所有待处理的事件
        for event in pygame.event.get():
            # 处理窗口关闭事件
            if event.type == pygame.QUIT:
                return False  # 用户点击窗口关闭按钮，退出程序
            
            # 处理键盘按键事件
            elif event.type == pygame.KEYDOWN:
                # 检测Shift键状态，用于控制旋转方向
                keys = pygame.key.get_pressed()
                is_shift_pressed = keys[pygame.K_LSHIFT] or keys[pygame.K_RSHIFT]
                clockwise = not is_shift_pressed  # Shift键按下时为逆时针
                
                if event.key == pygame.K_ESCAPE:
                    return False  # ESC键退出程序
                elif event.key == pygame.K_SPACE:
                    # 空格键切换自动旋转模式
                    self.auto_rotate = not self.auto_rotate
                    print(f"自动旋转: {'开启' if self.auto_rotate else '关闭'}")
                elif event.key == pygame.K_r:
                    # R键旋转右面（红色面）
                    self.rotate_face('right', clockwise)
                elif event.key == pygame.K_l:
                    # L键旋转左面（橙色面）
                    self.rotate_face('left', clockwise)
                elif event.key == pygame.K_u:
                    # U键旋转上面（白色面）
                    self.rotate_face('top', clockwise)
                elif event.key == pygame.K_d:
                    # D键旋转下面（黄色面）
                    self.rotate_face('bottom', clockwise)
                elif event.key == pygame.K_f:
                    # F键旋转前面（绿色面）
                    self.rotate_face('front', clockwise)
                elif event.key == pygame.K_b:
                    # B键旋转后面（蓝色面）
                    self.rotate_face('back', clockwise)
                
                # 方向键控制整个魔方的旋转（带动画效果）
                # 固定路线：绿→白→蓝→黄→绿（上键）和 绿→黄→蓝→白→绿（下键）
                elif event.key == pygame.K_UP:
                    # 上方向键：按绿→白→蓝→黄路线旋转
                    self.start_view_rotation('x', 1)
                elif event.key == pygame.K_DOWN:
                    # 下方向键：按绿→黄→蓝→白路线旋转
                    self.start_view_rotation('x', -1)
                elif event.key == pygame.K_LEFT:
                    # 左方向键：向左旋转（绕Y轴负方向90度）
                    self.start_view_rotation('y', -1)
                elif event.key == pygame.K_RIGHT:
                    # 右方向键：向右旋转（绕Y轴正方向90度）
                    self.start_view_rotation('y', 1)
            
            # 处理鼠标移动事件（拖拽旋转）
            elif event.type == pygame.MOUSEMOTION:
                # 检查是否按住左键拖拽
                if pygame.mouse.get_pressed()[0]:  # 左键按下状态
                    # 根据鼠标移动距离调整旋转角度
                    # event.rel[0]: 水平移动距离 -> Y轴旋转（左右转动）
                    # event.rel[1]: 垂直移动距离 -> X轴旋转（上下转动）
                    self.rotation_y += event.rel[0] * 0.5  # 水平拖拽控制左右旋转
                    self.rotation_x += event.rel[1] * 0.5  # 垂直拖拽控制上下旋转
        
        return True  # 继续运行程序
    
    def update(self):
        """
        更新游戏状态
        
        在每一帧中更新魔方的动态状态，包括：
        1. 自动旋转动画的角度更新
        2. 魔方面旋转动画的处理
        3. 其他可能的动画效果
        
        该方法在主循环中每帧调用一次，确保动画的流畅性
        """
        # 更新魔方面旋转动画
        if self.is_animating:
            # 根据旋转方向更新动画角度
            if self.animation_clockwise:
                self.animation_angle += self.animation_speed
            else:
                self.animation_angle -= self.animation_speed
            
            # 检查动画是否完成
            if abs(self.animation_angle) >= self.animation_target:
                # 动画完成，重置状态
                self.animation_angle = self.animation_target if self.animation_clockwise else -self.animation_target
                self.is_animating = False
                print(f"{self.animation_face} 面旋转动画完成")
                
                # 这里可以添加实际的魔方状态更新逻辑
                # 例如：self.update_cube_state(self.animation_face, self.animation_clockwise)
        
        # 更新视角旋转动画
        if self.is_view_animating:
            # 更新动画进度
            self.view_animation_progress += self.view_animation_speed
            
            # 使用缓动函数（ease-out）使动画更自然
            # 使用二次函数实现缓出效果
            eased_progress = 1 - (1 - self.view_animation_progress) ** 2
            
            # 根据进度插值计算当前角度
            self.rotation_x = self.view_animation_start_x + (self.view_animation_target_x - self.view_animation_start_x) * eased_progress
            self.rotation_y = self.view_animation_start_y + (self.view_animation_target_y - self.view_animation_start_y) * eased_progress
            
            # 检查动画是否完成
            if self.view_animation_progress >= 1.0:
                # 动画完成，设置最终角度并重置状态
                self.rotation_x = self.view_animation_target_x
                self.rotation_y = self.view_animation_target_y
                self.is_view_animating = False
                print(f"视角{'上下' if self.view_animation_axis == 'x' else '左右'}旋转动画完成")
        
        # 如果启用了自动旋转模式，持续更新旋转角度
        if self.auto_rotate:
            # 更新Y轴旋转角度（水平旋转），提供主要的旋转效果
            self.rotation_y += self.rotation_speed
            
            # 更新X轴旋转角度（垂直旋转），速度较慢，增加视觉层次
            self.rotation_x += self.rotation_speed * 0.5
            
            # 防止角度值过大，保持在合理范围内（可选优化）
            # 虽然OpenGL可以处理大角度值，但保持在0-360度范围内更清晰
            if self.rotation_y >= 360:
                self.rotation_y -= 360
            if self.rotation_x >= 360:
                self.rotation_x -= 360
    
    def render(self):
        """
        渲染3D魔方场景
        
        渲染流程：
        1. 清除屏幕缓冲区
        2. 设置视角变换矩阵
        3. 应用旋转变换
        4. 绘制魔方
        5. 恢复矩阵状态
        6. 交换缓冲区显示结果
        """
        # 清除颜色缓冲区和深度缓冲区，准备绘制新的一帧
        # GL_COLOR_BUFFER_BIT: 清除颜色信息
        # GL_DEPTH_BUFFER_BIT: 清除深度信息（Z缓冲，用于正确的3D遮挡）
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        
        # 保存当前的变换矩阵状态
        glPushMatrix()
        
        # 应用旋转变换，实现交互式视角控制
        # 先应用X轴旋转（上下拖拽的效果）
        glRotatef(self.rotation_x, 1, 0, 0)  # 绕X轴旋转，控制俯仰角度
        # 再应用Y轴旋转（左右拖拽的效果）
        glRotatef(self.rotation_y, 0, 1, 0)  # 绕Y轴旋转，控制方位角度
        
        # 绘制整个魔方（包括27个小立方体和缝隙）
        self.draw_cube()
        
        # 恢复之前保存的变换矩阵状态
        # 确保下一帧的渲染不会受到当前变换的影响
        glPopMatrix()
        
        # 交换前后缓冲区，将渲染结果显示到屏幕
        # 使用双缓冲技术避免画面闪烁
        pygame.display.flip()
    
    def run(self):
        """
        主程序运行循环
        
        实现游戏主循环的标准模式：
        1. 事件处理 - 响应用户输入
        2. 逻辑更新 - 更新游戏状态
        3. 渲染绘制 - 绘制当前帧
        4. 帧率控制 - 维持稳定的60FPS
        
        循环直到用户退出程序
        """
        # 创建时钟对象，用于控制帧率
        clock = pygame.time.Clock()
        running = True  # 程序运行状态标志
        
        print("=== 3D魔方控制说明 ===")
        print("魔方摆放: 白色朝上，绿色朝向用户")
        print("鼠标拖拽: 旋转视角")
        print("方向键: 控制魔方整体旋转（90度动画）")
        print("  ↑ - 固定路线旋转（绿→白→蓝→黄）  ↓ - 固定路线旋转（绿→黄→蓝→白）")
        print("  ← - 魔方向左旋转90度    → - 魔方向右旋转90度")
        print("空格键: 开启/关闭自动旋转")
        print("按键操作:")
        print("  F - 前面(绿色)  B - 后面(蓝色)")
        print("  R - 右面(红色)  L - 左面(橙色)")
        print("  U - 上面(白色)  D - 下面(黄色)")
        print("旋转方向:")
        print("  单独按键 - 顺时针旋转")
        print("  Shift+按键 - 逆时针旋转")
        print("ESC: 退出程序")
        print("========================")
        
        # 主游戏循环
        while running:
            # 1. 处理用户输入事件（键盘、鼠标、窗口事件）
            running = self.handle_events()
            
            # 2. 更新游戏逻辑状态（自动旋转等）
            self.update()
            
            # 3. 渲染当前帧到屏幕
            self.render()
            
            # 4. 控制帧率为60FPS，确保流畅的动画效果
            clock.tick(60)
        
        # 程序退出时的清理工作
        print("正在退出魔方程序...")
        pygame.quit()  # 关闭pygame
    


def main():
    """
    程序主入口函数
    
    负责：
    1. 初始化3D魔方可视化程序
    2. 处理程序启动和异常情况
    3. 提供用户友好的错误信息
    4. 确保程序的优雅退出
    
    异常处理：
    - KeyboardInterrupt: 用户按Ctrl+C强制退出
    - 其他异常: 显示错误信息并安全退出
    """
    print("=== 3D魔方可视化程序 ===")
    print("正在初始化OpenGL和pygame...")
    print("启动3D魔方渲染...")
    
    try:
        # 创建3D魔方实例并运行主循环
        cube_3d = RubiksCube3D()
        cube_3d.run()
        
    except KeyboardInterrupt:
        # 用户按Ctrl+C强制退出
        print("\n用户中断，程序已安全退出")
        
    except ImportError as e:
        # 缺少必要的库
        print(f"\n导入错误: {e}")
        print("请确保已安装所需的库：")
        print("pip install PyOpenGL PyOpenGL_accelerate pygame")
        
    except Exception as e:
        # 其他未预期的错误
        print(f"\n程序运行出错: {e}")
        print("请检查系统是否支持OpenGL，或尝试更新显卡驱动")
        
    finally:
        # 确保程序总是能够优雅退出
        print("程序结束")

if __name__ == "__main__":
    main()